#pragma once

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

class Sock
{
    const static int backlog = 20;

public:
    int Socket()
    {
        int n = socket(AF_INET, SOCK_STREAM, 0);
        if (n < 0)
        {
            printf("create sock fail  [errno: %d,  strerror: %s]", errno, strerror(errno));
            exit(1);
        }
        int opt = 1;
        setsockopt(n, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        return n;
    }

    void Bind(int sockfd, const std::string &ip, uint16_t port)
    {
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = ip.empty() ? INADDR_ANY : inet_addr(ip.c_str());

        int n = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
        if (n < 0)
        {
            printf("bind fail  [errno: %d,  strerror: %s]", errno, strerror(errno));
            exit(2);
        }
    }

    void Listen(int sockfd)
    {
        int n = listen(sockfd, backlog);
        if (n < 0)
        {
            printf("Listen fail  [errno: %d,  strerror: %s]", errno, strerror(errno));
            exit(3);
        }
    }

    int Accept(int sockfd, std::string*ip, uint16_t* port)
    {
        struct sockaddr_in addr;
        socklen_t addrlen = sizeof(addr);
        int serverSockfd = accept(sockfd, (struct sockaddr*)&addr, &addrlen);
        if (serverSockfd < 0)
        {
            printf("Accept fail  [errno: %d,  strerror: %s]", errno, strerror(errno));
            return -1;
        }

        if (ip)
            *ip = inet_ntoa(addr.sin_addr);
        if (port)
            *port = ntohs(addr.sin_port);

        return serverSockfd;
    }

    int Connect(int sockfd, const std::string& ip, const uint16_t& port)
    {
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());

        int n = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
        return n;
    }

    int Send(int sockfd, const std::string& mes)
    {
        ssize_t n = send(sockfd, mes.c_str(), mes.size(), 0);
        return n;
    }

    int Revc(int sockfd, std::string& mes)
    {
        char buffer[1024];
        int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        buffer[n] = 0;
        mes = buffer;
        
        return n;
    }
};